<?php

namespace App\Services;

use App\Common\Enums\AdvAliasEnum;
use App\Common\Enums\CpTypeEnums;
use App\Common\Enums\PlatformEnum;
use App\Common\Helpers\Advs;
use App\Common\Helpers\Functions;
use App\Common\Models\ConvertCallbackStrategyGroupModel;
use App\Common\Models\ConvertCallbackStrategyModel;
use App\Common\Services\BaseService;
use App\Common\Services\ErrorLogService;
use App\Common\Services\SystemApi\CenterApiService;
use App\Common\Services\SystemApi\NoticeApiService;
use App\Common\Services\SystemApi\UnionApiService;
use App\Common\Tools\CustomException;
use App\Enums\Ocean\OceanProjectStatusEnum;
use App\Models\Ocean\ChannelProjectLogModel;
use App\Models\Ocean\ChannelProjectModel;
use App\Models\Ocean\OceanAccountModel;
use App\Models\Ocean\OceanProjectModel;
use App\Models\Ocean\OceanPromotionModel;
use App\Models\Ocean\OceanV1AccountModel;
use App\Services\Ocean\OceanService;
use Illuminate\Support\Facades\DB;

class ChannelProjectService extends BaseService
{
    /**
     * @param $data
     * @return bool
     * @throws CustomException
     * 批量更新
     */
    public function batchUpdate($data){
        throw new CustomException([
            'code' => 'REFUSE',
            'message' => '禁止访问',
        ]);

        $this->validRule($data, [
            'channel_id' => 'required|integer',
            'project_ids' => 'required|array',
            'channel' => 'required',
            'platform' => 'required'
        ]);

        Functions::hasEnum(PlatformEnum::class, $data['platform']);

        DB::beginTransaction();

        try{
            foreach($data['project_ids'] as $projectId){
                $this->update([
                    'project_id' => $projectId,
                    'channel_id' => $data['channel_id'],
                    'platform' => $data['platform'],
                    'extends' => [
                        'channel' => $data['channel'],
                    ],
                ]);
            }
        }catch(CustomException $e){
            DB::rollBack();
            throw $e;
        }catch(\Exception $e){
            DB::rollBack();
            throw $e;
        }

        DB::commit();

        return true;
    }

    /**
     * @param $data
     * @return bool
     * 更新
     */
    public function update($data){
        $channelProjectModel = new ChannelProjectModel();
        $channelProject = $channelProjectModel->where('project_id', $data['project_id'])
            ->where('platform', $data['platform'])
            ->first();

        $flag = $this->buildFlag($channelProject);
        if(empty($channelProject)){
            $channelProject = new ChannelProjectModel();
        }

        $channelProject->project_id = $data['project_id'];
        $channelProject->channel_id = $data['channel_id'];
        $channelProject->platform = $data['platform'];
        $channelProject->extends = $data['extends'];
        $ret = $channelProject->save();

        if($ret && !empty($channelProject->id) && $flag != $this->buildFlag($channelProject)){
            // 账户管理员id
            if(!empty($data['account_id'])){
                $oceanV1AccountModel = new OceanV1AccountModel();
                $oceanV1Account = $oceanV1AccountModel->where('account_id', $data['account_id'])->first();
                if(!empty($oceanV1Account)){
                    $data['extends']['account_admin_id'] = $oceanV1Account->admin_id;
                }
            }

            $this->createChannelProjectLog([
                'channel_project_id' => $channelProject->id,
                'project_id' => $data['project_id'],
                'channel_id' => $data['channel_id'],
                'platform' => $data['platform'],
                'extends' => $data['extends'],
            ]);
        }

        return $ret;
    }

    /**
     * @param $channelProject
     * @return string
     * 构建标识
     */
    protected function buildFlag($channelProject){
        $adminId = !empty($channelProject->extends->channel->admin_id) ? $channelProject->extends->channel->admin_id : 0;
        $supportAdminId = !empty($channelProject->extends->support_admin_id) ? $channelProject->extends->support_admin_id : 0;
        if(empty($channelProject)){
            $flag = '';
        }else{
            $flag = implode("_", [
                $channelProject->project_id,
                $channelProject->channel_id,
                $channelProject->platform,
                $adminId,
                $supportAdminId,
            ]);
        }
        return $flag;
    }

    /**
     * @param $data
     * @return bool
     * 创建渠道-项目日志
     */
    protected function createChannelProjectLog($data){
        $channelProjectLogModel = new ChannelProjectLogModel();
        $channelProjectLogModel->channel_project_id = $data['channel_project_id'];
        $channelProjectLogModel->project_id = $data['project_id'];
        $channelProjectLogModel->channel_id = $data['channel_id'];
        $channelProjectLogModel->platform = $data['platform'];
        $channelProjectLogModel->extends = $data['extends'];
        return $channelProjectLogModel->save();
    }



    /**
     * @param $appId
     * @return |null
     * 按appid获取产品
     */
    public function getProductByAppId($appId){
        $appId = trim($appId);
        $db = 'n8_union';
        $sql = "SELECT `id`,`name`,`cp_type`,`cp_product_alias` FROM {$db}.products WHERE `app_id` = '{$appId}' LIMIT 1";
        $items = $this->execSql($sql);

        $ret = null;
        if(!empty($items)){
            $ret = $items[0];
        }

        return $ret;
    }

    /**
     * @param $productId
     * @param $cpChannelId
     * @return |null
     * 按cp渠道id获取渠道
     */
    public function getChannelByCpChannelId($productId, $cpChannelId){
        $cpChannelId = trim($cpChannelId);
        $db = Functions::isLocal() ? 'ny_union' : 'n8_union';
        $sql = "SELECT `id`,`name` FROM {$db}.channels WHERE product_id = {$productId} AND cp_channel_id = '{$cpChannelId}' LIMIT 1";
        $items = $this->execSql($sql);

        $ret = null;
        if(!empty($items)){
            $ret = $items[0];
        }

        return $ret;
    }

    /**
     * @param $channelId
     * @return |null
     * 获取渠道
     */
    public function getChannelByChannelId($channelId){
        $channelId = trim($channelId);
        $db = 'n8_union';
        $sql = "
            SELECT c.`id`, c.`name`, c.`product_id`, c.`book_id`, ce.admin_id FROM {$db}.channels c
            LEFT JOIN {$db}.channel_extends ce on c.id = ce.channel_id
            WHERE c.`id` = {$channelId}
            LIMIT 1
        ";
        $items = $this->execSql($sql);

        $ret = null;
        if(!empty($items)){
            $ret = $items[0];
        }

        return $ret;
    }

    /**
     * @param $oceanPromotion
     * @return mixed
     * @throws CustomException
     * 按广告获取渠道id
     */
    public function getChannelIdByPromotion($oceanPromotion){
        $promotionId = $oceanPromotion->id;
        if(!empty($oceanPromotion->extends->promotion_materials->mini_program_info)){
            // 直跳
            $jumpType = 'micropp';
            $microappUrl = $oceanPromotion->extends->promotion_materials->mini_program_info->url;

            // 兼容
            if(empty($microappUrl) && !empty($oceanPromotion->extends->promotion_materials->mini_program_info->params)){
                $microappUrl = 'page/index/index?'. $oceanPromotion->extends->promotion_materials->mini_program_info->params;
                if(!empty($oceanPromotion->extends->promotion_materials->mini_program_info->app_id)){
                    $microappUrl .= '&app_id='. $oceanPromotion->extends->promotion_materials->mini_program_info->app_id;
                }
            }

            $ret = parse_url($microappUrl);
            if(!isset($ret['query'])){
                throw new CustomException([
                    'code' => 'QUERY_IS_EMPTY',
                    'message' => "query is empty",
                    'log' => true,
                    'data' => [
                        'promotion_id' => $promotionId,
                        'jump_type' => $jumpType,
                    ],
                ]);
            }
            $query = urldecode($ret['query']);
            parse_str($query, $rett);
            $appId = $rett['app_id'];

            $product = $this->getProductByAppId($appId);
            if(empty($product)){
                throw new CustomException([
                    'code' => 'UNKNOWN_PRODUCT',
                    'message' => "unknown product, app_id:{$appId}",
                    'log' => true,
                    'data' => [
                        'promotion_id' => $promotionId,
                        'jump_type' => $jumpType,
                        'app_id' => $appId,
                    ],
                ]);
            }

            if($product['cp_type'] == CpTypeEnums::KY){
                // 兼容
                if(empty($rett['spread_id']) && !empty($rett['start_page'])){
                    $ret = parse_url($rett['start_page']);
                    if(!isset($ret['query'])){
                        throw new CustomException([
                            'code' => 'QUERY_IS_EMPTY',
                            'message' => "query is empty",
                            'log' => true,
                            'data' => [
                                'promotion_id' => $promotionId,
                                'jump_type' => $jumpType,
                            ],
                        ]);
                    }
                    $query = urldecode($ret['query']);
                    parse_str($query, $rett);
                }

                if(empty($rett['spread_id'])){
                    throw new CustomException([
                        'code' => 'QUERY_SPREAD_ID_IS_EMPTY',
                        'message' => "query spread_id is empty, query:{$query}",
                        'log' => true,
                        'data' => [
                            'promotion_id' => $promotionId,
                            'jump_type' => $jumpType,
                            'query' => $query,
                        ],
                    ]);
                }
                $cpChannelId = $rett['spread_id'];
            }else{
                throw new CustomException([
                    'code' => 'UNKNOWN_CP_TYPE',
                    'message' => "unknown cp_type, cp_type:{$product['cp_type']}",
                    'log' => true,
                    'data' => [
                        'promotion_id' => $promotionId,
                        'jump_type' => $jumpType,
                        'cp_type' => $product['cp_type'],
                    ],
                ]);
            }
        }else{
            // 落地页
            $jumpType = 'site';
            $pageUrl = $oceanPromotion->extends->promotion_materials->external_url_material_list[0];

            $ret = parse_url($pageUrl);

            $tmp = explode("/", trim($ret['path'], '/'));
            $siteId = end($tmp);

            $accountId = $oceanPromotion->account_id;

            $siteMap = [
                '落地页ID' => '账户ID',
                '7372131544015732787' => '1789052723408042',
                '7352818005216428042' => '1789050827722763',
                '7373842180261822515' => '1783061609594889',
            ];

            if(isset($siteMap[$siteId])){
                $accountId = $siteMap[$siteId];
            }

            // 获取站点预览链接
            $oceanAccountModel = new OceanAccountModel();
            $oceanAccount = $oceanAccountModel->where('account_id', $accountId)->first();
            $oceanService = new OceanService($oceanAccount->app_id);
            $oceanService->setAccountId($oceanAccount->account_id);
            $oceanService->sdk->setAccessToken($oceanAccount->access_token);

            if(empty($siteId)){
                throw new CustomException([
                    'code' => 'SITE_ID_IS_EMPTY',
                    'message' => "stie_id is empty",
                    'log' => true,
                    'data' => [
                        'page_url' => $pageUrl,
                    ],
                ]);
            }

            $data = $oceanService->sdk->getSitePreview($oceanAccount->account_id, $siteId);

            $content = file_get_contents($data['url']);

            preg_match("/\"userName\":\"(\w+)\"/", $content, $mm);
            if(empty($mm[1]) || strpos($mm[1], 'gh_') === false){
                throw new CustomException([
                    'code' => 'UNMATCH_APP_ID',
                    'message' => "unmatch app_id",
                    'log' => true,
                    'data' => [
                        'promotion_id' => $promotionId,
                        'jump_type' => $jumpType,
                        'url' => $data['url'],
                        'mm' => $mm,
                    ],
                ]);
            }
            $appId = $mm[1];

            $product = $this->getProductByAppId($appId);

            if(empty($product)){
                if(empty($product)){
                    throw new CustomException([
                        'code' => 'UNKNOWN_PRODUCT',
                        'message' => "unknown product, app_id:{$appId}",
                        'log' => true,
                        'data' => [
                            'promotion_id' => $promotionId,
                            'jump_type' => $jumpType,
                            'app_id' => $appId,
                        ],
                    ]);
                }
            }

            if($product['cp_type'] == CpTypeEnums::KY){
                preg_match("/spread_id=(\d+)/", $content, $m);
                if(empty($m[1])){
                    throw new CustomException([
                        'code' => 'UNMATCH_KY_SPREAD_ID',
                        'message' => "unmatch KY spread_id",
                        'log' => true,
                        'data' => [
                            'promotion_id' => $promotionId,
                            'jump_type' => $jumpType,
                            'url' => $data['url'],
                            'm' => $m,
                        ],
                    ]);
                }

                $cpChannelId = $m[1];
            }else{
                throw new CustomException([
                    'code' => 'UNKNOWN_CP_TYPE',
                    'message' => "unknown cp_type, cp_type:{$product['cp_type']}",
                    'log' => true,
                    'data' => [
                        'promotion_id' => $promotionId,
                        'jump_type' => $jumpType,
                        'cp_type' => $product['cp_type'],
                    ],
                ]);
            }
        }

        if(empty($cpChannelId)){
            var_dump("error, cp_channel_id is empty");

            throw new CustomException([
                'code' => 'CP_CHANNEL_ID_IS_EMPTY',
                'message' => "cp_channel_id is empty",
                'log' => true,
                'data' => [
                    'promotion_id' => $promotionId,
                    'jump_type' => $jumpType,
                ],
            ]);
        }

        $channel = $this->getChannelByCpChannelId($product['id'], $cpChannelId);
        if(empty($channel)){
            throw new CustomException([
                'code' => 'NOT_FOUND_CHANNEL',
                'message' => "not found channel, product_id:{$product['id']}, cp_channel_id:{$cpChannelId}",
                'log' => true,
                'data' => [
                    'promotion_id' => $promotionId,
                    'jump_type' => $jumpType,
                    'product_id' => $product['id'],
                    'cp_channel_id' => $cpChannelId,
                ],
            ]);
        }
        $channelId = $channel['id'];

        return $channelId;
    }

    /**
     * @param $param
     * @return bool
     * 同步
     */
    public function sync($param){
        $date = $param['date'];
        var_dump("date: {$date}");

        $projectId = $param['project_id'] ?? '';
        $accountId = $param['account_id'] ?? '';
        $fixHistory = $param['fix_history'] ?? '';

        $startTime = date('Y-m-d H:i:s', strtotime('-2 hours', strtotime($date)));
        $endTime = "{$date} 23:59:59";

        if(!empty($fixHistory)){
            $fixHistory = explode(",", $fixHistory);
            if(is_array($fixHistory) && count($fixHistory) == 2){
                var_dump("get data for fix history: {$fixHistory[0]} - $fixHistory[1]");
                $sql = "
                    SELECT account_id
                    FROM ocean_projects
                    WHERE id IN (
                        SELECT project_id
                        FROM ocean_promotion_reports
                        WHERE stat_datetime BETWEEN '{$fixHistory[0]}' AND '{$fixHistory[1]}'
                        AND stat_cost > 0 AND project_id NOT IN (
                            SELECT project_id FROM channel_project_logs
                        )
                        GROUP BY project_id
                    )
                    GROUP BY account_id
                ";
                $fixItems = $this->execSql($sql);
                //Functions::consoleDump($fixItems);
                $actIds = [];
                foreach($fixItems as $fixItem){
                    $actIds[] = $fixItem['account_id'];
                }
                $oceanProjectModel = new OceanProjectModel();
                $oceanProjects = $oceanProjectModel
                    ->whereIn('account_id', $actIds)
                    ->get();
            }
        }elseif(!empty($accountId)){
            var_dump("get data for account_id:". $accountId);
            $oceanProjectModel = new OceanProjectModel();
            $oceanProjects = $oceanProjectModel
                ->where('account_id', $accountId)
                ->get();
        }elseif(!empty($projectId)){
            var_dump("get data for project_id:". $projectId);
            $oceanProjectModel = new OceanProjectModel();
            $oceanProjects = $oceanProjectModel
                ->where('id', $projectId)
                ->get();
        }else{
            $oceanProjectModel = new OceanProjectModel();
            $oceanProjects = $oceanProjectModel
                ->whereRaw(DB::raw("(
                project_modify_time BETWEEN '{$startTime}' AND '{$endTime}' OR
                id IN (
                    SELECT project_id FROM ocean_promotions
                    WHERE promotion_modify_time BETWEEN '{$startTime}' AND '{$endTime}'
                    GROUP BY project_id
                )
            )"))
            ->orderBy('created_at', 'desc')
            ->get();
        }

        var_dump("project count:". $oceanProjects->count());

        $errorMsgMap = [];
        foreach($oceanProjects as $oceanProject){
            if(!empty($fixHistory)){
                $channelProjectLogModel = new ChannelProjectLogModel();
                $channelProjectLog = $channelProjectLogModel->where('project_id', $oceanProject['id'])->first();
                if(!empty($channelProjectLog)){
                    var_dump("已存在渠道-项目关联, project_id:{$oceanProject['id']}");
                    continue;
                }
            }

            $oceanPromotionModel = new OceanPromotionModel();
            $oceanPromotions = $oceanPromotionModel
                ->where('project_id', $oceanProject->id)
                ->orderBy('promotion_modify_time', 'desc')
                ->take(20)
                ->get();

            var_dump("promotion count:". $oceanPromotions->count());

            foreach($oceanPromotions as $oceanPromotion){
                try{
                    $channelId = $this->getChannelIdByPromotion($oceanPromotion);
                    $channel = $this->getChannelByChannelId($channelId);
                }catch(CustomException $e){
                    $errorInfo = $e->getErrorInfo();
                    $errorMsg = $errorInfo->message;
                    var_dump("get channel_id by promotion error: {$errorMsg}");

                    if($errorMsg == '公共请求错误' || !isset($errorMsgMap[$errorMsg])){
                        $errorLogService = new ErrorLogService();
                        $errorLogService->catch($e);
                        $errorMsgMap[$errorMsg] = 1;
                    }

                    if($errorInfo->code == 'UNKNOWN_PRODUCT'){
                        break;
                    }

                    continue;
                }

                var_dump("project_id: {$oceanProject->id}, channel_id: {$channelId}");

                // 更新
                $this->update([
                    'account_id' => $oceanProject->account_id,
                    'project_id' => $oceanProject->id,
                    'channel_id' => $channelId,
                    'platform' => PlatformEnum::DEFAULT,
                    'extends' => [
                        'by_promotion_id' => $oceanPromotion->id,
                        'channel' => $channel,
                    ],
                ]);
                break;
            }
        }

        return true;
    }

    /**
     * @param $param
     * @return bool
     * @throws CustomException
     * 备份_同步
     */
    public function bak_sync($param){
        $date = $param['date'];

        $startTime = date('Y-m-d H:i:s', strtotime('-2 hours', strtotime($date)));
        $endTime = "{$date} 23:59:59";

        $oceanProjectModel = new OceanProjectModel();
        $oceanProjects = $oceanProjectModel->whereBetween('project_modify_time', [$startTime, $endTime])->get();

        $keyword = 'sign='. Advs::getAdvClickSign(AdvAliasEnum::OCEAN_V2);

        foreach($oceanProjects as $oceanProject){
            $actionTrackUrls = $oceanProject->extends->track_url_setting->action_track_url ?? [];
            $hasKeyword = false;
            foreach($actionTrackUrls as $actionTrackUrl){
                if(empty($actionTrackUrl)){
                    continue;
                }

                if(strpos($actionTrackUrl, $keyword) === false){
                    continue;
                }

Functions::isLocal() && $actionTrackUrl .= '&support_admin_id=1';

                $ret = parse_url($actionTrackUrl);
                parse_str($ret['query'], $param);

                $unionApiService = new UnionApiService();

                if(!empty($param['android_channel_id'])){
                    $channel = $unionApiService->apiReadChannel(['id' => $param['android_channel_id']]);
                    $chanenlExtends = $channel['channel_extends'] ?? [];
                    $channel['admin_id'] = $chanenlExtends['admin_id'] ?? 0;
                    unset($channel['extends']);
                    unset($channel['channel_extends']);

                    // 助理管理员id
                    $supportAdminId = 0;

                    $centerApiService = new CenterApiService();
                    $channelAdminUser = $centerApiService->apiReadAdminUser($channel['admin_id']);

                    // 助理
                    $hasParent = !empty($chanenlExtends['parent_id']);
                    $isSupport = !empty($channelAdminUser['is_support']);
                    if($hasParent && $isSupport){
                        $parentChannel = $unionApiService->apiReadChannel(['id' => $chanenlExtends['parent_id']]);
                        $supportAdminId = $channel['admin_id'];
                        $channel['admin_id'] = $parentChannel['channel_extends']['admin_id'];
                    }

                    $this->update([
                        'project_id' => $oceanProject->id,
                        'channel_id' => $param['android_channel_id'],
                        'platform' => PlatformEnum::ANDROID,
                        'extends' => [
                            'action_track_url' => $actionTrackUrl,
                            'channel' => $channel,
                            'support_admin_id' => $supportAdminId,
                        ],
                    ]);
                }

                if(!empty($param['ios_channel_id'])){
                    $channel = $unionApiService->apiReadChannel(['id' => $param['ios_channel_id']]);
                    $chanenlExtends = $channel['channel_extends'] ?? [];
                    $channel['admin_id'] = $chanenlExtends['admin_id'] ?? 0;
                    unset($channel['extends']);
                    unset($channel['channel_extends']);

                    // 助理管理员id
                    $supportAdminId = 0;

                    $centerApiService = new CenterApiService();
                    $channelAdminUser = $centerApiService->apiReadAdminUser($channel['admin_id']);

                    // 助理
                    $hasParent = !empty($chanenlExtends['parent_id']);
                    $isSupport = !empty($channelAdminUser['is_support']);
                    if($hasParent && $isSupport){
                        $parentChannel = $unionApiService->apiReadChannel(['id' => $chanenlExtends['parent_id']]);
                        $supportAdminId = $channel['admin_id'];
                        $channel['admin_id'] = $parentChannel['channel_extends']['admin_id'];
                    }

                    $this->update([
                        'project_id' => $oceanProject->id,
                        'channel_id' => $param['ios_channel_id'],
                        'platform' => PlatformEnum::IOS,
                        'extends' => [
                            'action_track_url' => $actionTrackUrl,
                            'channel' => $channel,
                            'support_admin_id' => $supportAdminId,
                        ],
                    ]);
                }

                $hasKeyword = true;
            }

            // 不需通知状态
            $oceanProjectStatus = [
                OceanProjectStatusEnum::PROJECT_STATUS_DELETE,
                OceanProjectStatusEnum::PROJECT_STATUS_DISABLE,
            ];

            if(!in_array($oceanProject->status, $oceanProjectStatus) && !$hasKeyword){
                $oceanAccountModel = new OceanAccountModel();
                $oceanAccount = $oceanAccountModel->where('account_id', $oceanProject->account_id)->first();

                if(!Functions::isLocal() && !empty($oceanAccount->admin_id)){
                    $title = "项目监测链错误";
                    $c = [
                        "账户id: {$oceanAccount->account_id}",
                        "账户名称: {$oceanAccount->name}",
                        "项目id: {$oceanProject->id}",
                        "项目名称: {$oceanProject->name}",
                        "请在 联运系统 > 渠道 中复制正确监测链！！",
                    ];

                    $i = 1;
                    foreach($actionTrackUrls as $actionTrackUrl){
                        $c[] = "当前项目监测链{$i}:$actionTrackUrl}";
                        $i++;
                    }

                    $content = implode("<br>", $c);

                    $adminId = $oceanAccount->admin_id;

                    $noticeApiService = new NoticeApiService();
                    //$noticeApiService->apiSendFeishuMessage($title, $content, $adminId, 1800);
                }
            }
        }

        return true;
    }
}
